<?php

namespace Lib;

class Curl {
	private $_rConn = null;
	private $_aOpt = array ();
	private $_aPost = array ();
	private $_aFile = array ();
	private $_aHeader = array ();
	private $_sCookieFile = null;
	private $_aCookie = array ();

	/**
	 * 初始化CURL
	 */
	public function init() {
		try {
			if (! extension_loaded ( 'curl' )) {
				new ExceptionExt ( '缺少CURL扩展' );
			}
		} catch ( ExceptionExt $e ) {
			$e->showError ();
		}
		$this->_rConn = curl_init ();
	}

	/**
	 * 执行CURL
	 *
	 * @param 地址 $sUrl        	
	 * @return string
	 */
	public function exec($sUrl) {
		if (! $this->_rConn) {
			$this->init ();
		}
		$sUrl = str_replace ( '&amp;', '&', $sUrl );
		curl_setopt ( $this->_rConn, CURLOPT_URL, $sUrl );
		curl_setopt ( $this->_rConn, CURLOPT_RETURNTRANSFER, 1 );
		curl_setopt ( $this->_rConn, CURLOPT_POST, 1 );
		curl_setopt ( $this->_rConn, CURLOPT_POSTFIELDS, $this->_aPost + $this->_aFile );
		foreach ( $this->_aOpt as $k => $v ) {
			curl_setopt ( $this->_rConn, $k, $v );
		}
		if (! empty ( $this->_aHeader )) {
			foreach ( $this->_aHeader as $k => $v ) {
				$aHeader [] = $k . ':' . $v;
			}
			curl_setopt ( $this->_rConn, CURLOPT_HTTPHEADER, $aHeader );
		}
		if ($this->_sCookieFile) {
			$this->_writeCookie ( $sUrl );
			curl_setopt ( $this->_rConn, CURLOPT_COOKIEJAR, $this->_sCookieFile );
			curl_setopt ( $this->_rConn, CURLOPT_COOKIEFILE, $this->_sCookieFile );
		}
		$sHtml = curl_exec ( $this->_rConn );
		if (! $sHtml) {
			$aInfo = curl_getinfo ( $this->_rConn );
			if ($aInfo ['http_code'] == 301 || $aInfo ['http_code'] == 302) {
				$aHeader = get_headers ( $sUrl, 1 );
				$sLocation = $aHeader ['Location'] [0];
				if (preg_match ( '/^https?:\/\/.+/i', $sLocation )) {
					$sHtml = $this->exec ( $sLocation );
				} else {
					$aUrl = parse_url ( $sUrl );
					$sUrl = $aUrl ['scheme'] . '://' . $aUrl ['host'] . $sLocation;
					$sHtml = $this->exec ( $sUrl );
				}
			}
		}
		return $sHtml;
	}

	/**
	 * 设置
	 *
	 * @param 设置名 $sOpt        	
	 * @param 值 $sValue        	
	 */
	public function setOpt($sOpt, $sValue) {
		curl_setopt ( $sOpt, $sValue );
	}

	/**
	 * 添加POST
	 *
	 * @param 变量名 $sName        	
	 * @param 值 $sValue        	
	 */
	public function addPost($sName, $sValue) {
		$this->_aPost [$sName] = $sValue;
	}

	/**
	 * 添加上传文件
	 *
	 * @param 变量名 $sName        	
	 * @param 文件名 $sValue        	
	 */
	public function addFile($sName, $sFile) {
		if (is_file ( $sFile )) {
			$this->_aPost [$sName] = '@' . realpath ( $sFile );
		}
	}

	/**
	 * 添加页面头新信息
	 *
	 * @param 变量名 $sName        	
	 * @param 值 $sValue        	
	 */
	public function addHeader($sName, $sValue) {
		$this->_aHeader [$sName] = $sValue;
	}

	/**
	 * 重置设置
	 */
	public function resetOpt() {
		curl_setopt_array ( $this->_rConn, $this->_aOpt );
	}

	/**
	 * 清空设置
	 */
	public function clearOpt() {
		$this->_aOpt = array ();
	}

	/**
	 * 清空POST
	 */
	public function clearPost() {
		$this->_aPost = array ();
	}

	/**
	 * 清空上传文件
	 */
	public function clearFile() {
		$this->_aFile = array ();
	}

	/**
	 * 清空页面头信息
	 */
	public function clearHeader() {
		$this->_aHeader = array ();
	}

	/**
	 * 清理所有内容
	 */
	public function clear() {
		$this->clearOpt ();
		$this->clearPost ();
		$this->clearFile ();
		$this->clearHeader ();
	}

	/**
	 * 初始话Cookie
	 *
	 * @param Cookie文件名 $sFile        	
	 * @return string
	 */
	public function initCookie($sFile = null) {
		$this->_aCookie = array ();
		$sDir = ROOT . '/cache/cookie/';
		if ($sFile) {
			$this->_sCookieFile = $sDir . $sFile;
		}
		if (is_file ( $sDir . $sFile )) {
			$f = @fopen ( $sDir . $sFile, 'rb' );
			if ($f) {
				while ( ! feof ( $f ) ) {
					$sLine = trim ( fgets ( $f, 4096 ) );
					if (preg_match ( '/^#\s/si', $sLine )) {
						$this->_aCookie [] ['comment'] = $sLine;
					} elseif ($sLine) {
						list ( $aCookie ['domain'], $aCookie ['session'], $aCookie ['path'], $aCookie ['secure'], $aCookie ['expire'], $aCookie ['name'], $aCookie ['value'] ) = preg_split ( '/\s+/s', $sLine );
						$this->_aCookie [] = $aCookie;
					}
				}
			}
			return $sFile;
		}
		if ($sFile) {
			touch ( $sDir . $sFile );
		} else {
			$this->_sCookieFile = tempnam ( $sDir, 'cookie_' );
		}
		return basename ( $this->_sCookieFile );
	}

	private function _writeCookie($sUrl = null) {
		if ($this->_sCookieFile) {
			$sCookie = '';
			foreach ( $this->_aCookie as $v ) {
				if (isset ( $v ['comment'] )) {
					$sCookie .= $v ['comment'] . "\n";
				} else {
					$sCookie .= $v ['domain'] . "\t" . $v ['session'] . "\t" . $v ['path'] . "\t" . $v ['secure'] . "\t" . $v ['expire'] . "\t" . $v ['name'] . "\t" . $v ['value'] . "\n";
				}
			}
			
			if ($sUrl) {
				$aUrlInfo = parse_url ( $sUrl );
				if ($aUrlInfo ['host'] != 'localhost' && long2ip ( ip2long ( $aUrlInfo ['host'] ) ) != $aUrlInfo ['host']) {
					$aUrlInfo ['host'] = '.' . $aUrlInfo ['host'];
				}
				$sCookie = str_replace ( '{domain}', $aUrlInfo ['host'], $sCookie );
			}
			file_put_contents ( $this->_sCookieFile, $sCookie );
		}
	}

	/**
	 * 获取Cookie文件
	 *
	 * @return string
	 */
	public function getCookieFile() {
		return basename ( $this->_sCookieFile );
	}

	/**
	 * 设置Cookie
	 *
	 * @param 变量名 $sName        	
	 * @param 值 $sValue        	
	 * @param 有效期 $iExpire        	
	 * @param 域名 $sDomain        	
	 * @param 路径 $sPath        	
	 * @param 客户端只读 $bHttpOnly        	
	 * @param 安全连接 $bSecure        	
	 */
	public function setCookie($sName, $sValue, $iExpire = null, $sDomain = null, $sPath = null, $bHttpOnly = null, $bSecure = null) {
		if (! $this->_sCookieFile) {
			$this->initCookie ();
		}
		if ($sDomain && $sDomain != 'localhost' && substr ( $sDomain, 0, 1 ) == '.' && long2ip ( ip2long ( $sDomain ) ) != $sDomain) {
			$sDomain = '.' . $sDomain;
		}
		$bSet = false;
		foreach ( $this->_aCookie as $k => $v ) {
			if ($v ['name'] == $sName) {
				if (intval ( $iExpire ) > 0 && intval ( $iExpire ) < time ()) {
					unset ( $this->_aCookie [$k] );
				}
				$v ['value'] = $sValue;
				if ($iExpire !== null) {
					$v ['expire'] = $iExpire;
				}
				if ($bSecure !== null) {
					$v ['secure'] = $bSecure ? 'TRUE' : 'FALSE';
				}
				if ($sPath != null) {
					$v ['path'] = $sPath;
				}
				if ($sDomain !== null || $bHttpOnly !== null) {
					if ($bHttpOnly === true) {
						if (strpos ( $v ['domain'], '#HttpOnly_' ) === false) {
							if ($sDomain) {
								$v ['domain'] = '#HttpOnly_' . $sDomain;
							} else {
								$v ['domain'] = '#HttpOnly_' . $v ['domain'];
							}
						} elseif ($sDomain) {
							$v ['domain'] = '#HttpOnly_' . $sDomain;
						}
					} elseif ($bHttpOnly === false) {
						if ($sDomain) {
							$v ['domain'] = $sDomain;
						} else {
							$v ['domain'] = str_replace ( '#HttpOnly_', '', $v ['domain'] );
						}
					}
				}
				$this->_aCookie [$k] = $v;
				$bSet = true;
				break;
			}
		}
		
		if ($bSet == false) {
			$aCookie = array (
					'name' => $sName,
					'value' => $sValue,
					'expire' => intval ( $iExpire ),
					'session' => 'FALSE',
					'path' => $sPath ? $sPath : '/',
					'secure' => $bSecure ? 'TRUE' : 'FALSE' 
			);
			if ($bHttpOnly) {
				$aCookie ['domain'] = '#HttpOnly_';
			}
			if ($sDomain == null) {
				$sDomain = '{domain}';
			}
			$aCookie ['domain'] .= $sDomain;
			$this->_aCookie [] = $aCookie;
		}
	}

	/**
	 * 获取Cookie值
	 *
	 * @param 变量名 $sName        	
	 * @return string
	 */
	public function getCookie($sName) {
		if (! $this->_sCookieFile) {
			$this->initCookie ();
		}
		foreach ( $this->_aCookie as $v ) {
			if ($v ['name'] == 'name') {
				if ($v ['expire'] == 0 || $v ['expire'] > time ()) {
					return $v ['value'];
				} else {
					return null;
				}
			}
		}
		return null;
	}

	/**
	 * 获取内容
	 *
	 * @param 地址 $sUrl        	
	 * @param 参数 $aParam
	 *        	array{
	 *        	header：		页面头信息（数组）
	 *        	post：		post内容（数组）
	 *        	file：		上传文件（数组）
	 *        	opt：		参数（数组）
	 *        	cookiefile：	cookie文件
	 *        	cookie：		cookie内容（数组）
	 *        	}
	 * @return string
	 */
	public function get($sUrl, $aParam) {
		$this->clear ();
		$this->init ();
		if (isset ( $aParam ['post'] )) {
			if (is_array ( $aParam ['post'] )) {
				foreach ( $aParam ['post'] as $k => $v ) {
					$this->addPost ( $k, $v );
				}
			}
		}
		if (isset ( $aParam ['file'] )) {
			if (is_array ( $aParam ['file'] )) {
				foreach ( $aParam ['file'] as $k => $v ) {
					$this->addPost ( $k, $v );
				}
			}
		}
		if (isset ( $aParam ['opt'] )) {
			if (is_array ( $aParam ['opt'] )) {
				foreach ( $aParam ['opt'] as $k => $v ) {
					$this->addPost ( $k, $v );
				}
			}
		}
		if (isset ( $aParam ['header'] )) {
			if (is_array ( $aParam ['header'] )) {
				foreach ( $aParam ['header'] as $k => $v ) {
					$this->addHeader ( $k, $v );
				}
			}
		}
		if ($aParam ['cookiefile']) {
			$this->initCookie ( $aParam ['cookiefile'] );
		}
		if (isset ( $aParam ['cookie'] )) {
			if (is_array ( $aParam ['cookie'] )) {
				foreach ( $aParam ['cookie'] as $k => $v ) {
					$this->setCookie ( $k, $v );
				}
			}
		}
		$sHtml = $this->exec ( $sUrl );
		curl_close ( $this->_rConn );
		return $sHtml;
	}
}